package coldfilter

import (
	"gitee.com/sunny-zhangqiang/zcache/comm"
	bf "github.com/steakknife/bloomfilter"
	"hash"
	"hash/fnv"
	"sync/atomic"
	"time"
)

const (
	probCollide   = 0.0001
	hourPerFilter = 6 // 多久转动一次过滤器
)

// 保存n个过滤器, 每个过滤器维护hourPerFilter小时的数据.
// 每过hourPerFilter就转动一次.
// 过滤器个数 = 保存的天数 * 24 / hourPerFilter
// maxElements表示 hourPerFilter 周期内预计产生多少个不同的key.
type circleBloomFilter struct {
	index       int
	maxNum      int
	maxElements uint64
	updatePos   int
	filters     []*bf.Filter
}

type circleFilter struct {
	v atomic.Value
}

func newCircleFilter(index, num int, maxElements uint64, rotateInterval time.Duration) *circleFilter {
	cbf := &circleBloomFilter{
		index:       index,
		maxNum:      num,
		maxElements: maxElements,
		updatePos:   0,
		filters:     make([]*bf.Filter, 0, num),
	}

	filter, _ := bf.NewOptimal(maxElements, probCollide)
	cbf.filters = append(cbf.filters, filter)
	cf := &circleFilter{}
	cf.v.Store(cbf)
	go func() {
		for {
			<-time.After(rotateInterval)
			cf.update()
		}
	}()

	return cf
}

func (cf *circleFilter) update() {
	cbf := cf.v.Load().(*circleBloomFilter)
	newCbf := &circleBloomFilter{
		index:       cbf.index,
		maxNum:      cbf.maxNum,
		maxElements: cbf.maxElements,
		updatePos:   cbf.updatePos + 1,
	}

	filter, _ := bf.NewOptimal(cbf.maxElements, probCollide)
	defer func() {
		cf.v.Store(newCbf)
	}()

	if len(cbf.filters) < cbf.maxNum {
		newCbf.filters = append(cbf.filters, filter)
		return
	}
	newCbf.filters = make([]*bf.Filter, newCbf.maxNum)
	copy(newCbf.filters, cbf.filters)
	newCbf.updatePos %= newCbf.maxNum
	newCbf.filters[newCbf.updatePos] = filter
}

func transformKey(key string) hash.Hash64 {
	f := fnv.New64a()
	f.Write(comm.String2Bytes(key))
	return f
}

func (cf *circleFilter) contains(key string) bool {
	hash := transformKey(key)
	cbf := cf.v.Load().(*circleBloomFilter)
	for i := 0; i < len(cbf.filters); i++ {
		if cbf.filters[i].Contains(hash) {
			return true
		}
	}
	return false
}

func (cf *circleFilter) add(key string) {
	cbf := cf.v.Load().(*circleBloomFilter)
	cbf.filters[cbf.updatePos].Add(transformKey(key))
}

type ColdFilter struct {
	filters  []*circleFilter
	layerNum int
}

// NewColdFilter
// params: layerNum表示n天内查找多少次
// params: dayNum表示多少天内查找m次
// indexNum: 一个过滤器中有多少个元素
func NewColdFilter(layerNum int, dayNum int, indexNum uint64) *ColdFilter {
	cf := &ColdFilter{
		layerNum: layerNum,
		filters:  make([]*circleFilter, layerNum),
	}
	for i := 0; i < layerNum; i++ {
		// dayNum表示保存多少天内的数据, dayNum*24/hourPerFilter表示要使用多少个过滤器.
		// time.Hour*hourPerFilter表示多久转动一次过滤器. 每个过滤器存在time.Hour*hourPerFilter时间周期的数据.
		cf.filters[i] = newCircleFilter(i, dayNum*24/hourPerFilter, indexNum, time.Hour*hourPerFilter)
	}
	return cf
}

func (cf *ColdFilter) Add(key string) {
	// 已经达到最高层, 不再增加. 热度最多可维持多少天, 之后需要重新统计.
	if cf.filters[cf.layerNum-1].contains(key) {
		return
	}
	// 将对应的key写入下一层
	for i := 0; i < cf.layerNum; i++ {
		if !cf.filters[i].contains(key) {
			cf.filters[i].add(key)
			return
		}
	}
}

func (cf *ColdFilter) Contains(key string) bool {
	return cf.filters[cf.layerNum-1].contains(key)
}
